package com.neo.subscribe.common.util;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;

/**
 * @author sky91 - feitiandaxia1991@163.com
 */
public class BeanUtils {
	public static void copyDeclaredFields(Object dest, Class[] destClasses, Object src, Class[] srcClasses) {
		try {
			for(int i = 0; i < destClasses.length; i++) {
				Class destClass = destClasses[i];
				for(Field srcField : srcClasses[i].getDeclaredFields()) {
					try {
						srcField.setAccessible(true);
						Field destField = destClass.getDeclaredField(srcField.getName());
						destField.setAccessible(true);
						destField.set(dest, srcField.get(src));
					} catch(NoSuchFieldException ignored) {
					}
				}
			}
		} catch(IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}

	public static Object getProperty(Object bean, String name) {
		String methodNameSuffix = Character.toUpperCase(name.charAt(0)) + name.substring(1);
		Method method;
		Class<?> c = bean.getClass();
		try {
			method = c.getMethod("get" + methodNameSuffix);
		} catch(NoSuchMethodException e) {
			try {
				method = c.getMethod("is" + methodNameSuffix);
			} catch(NoSuchMethodException e1) {
				try {
					Field field = c.getField(name);
					return field.get(bean);
				} catch(NoSuchFieldException e2) {
					return null;
				} catch(IllegalAccessException e2) {
					throw new RuntimeException(e);
				}
			}
		}
		try {
			return method.invoke(bean);
		} catch(IllegalAccessException | InvocationTargetException e) {
			throw new RuntimeException(e);
		}
	}

	public static void setProperty(Object bean, String name, Object value) {
		try {
			org.apache.commons.beanutils.BeanUtils.setProperty(bean, name, value);
		} catch(IllegalAccessException | InvocationTargetException e) {
			throw new RuntimeException(e);
		}
	}

	public static <T> T setValue(T obj,Object... values) {
		for (int i = 0 ;i<values.length;i += 2) {
			try {
				PropertyUtils.setProperty(obj,(String)values[i],values[i+1]);
			} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
				throw new RuntimeException(e);
			}
		}
		return obj;
	}

	public static <T> T setValueNotNull(T obj,Object... values) {
		for (int i = 0 ;i<values.length;i += 2) {
            Object t = values[i + 1];
			try {
				if (t != null)
					PropertyUtils.setProperty(obj, (String) values[i], t);
			} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
				throw new RuntimeException(e);
			}
		}
		return obj;
	}



	public static <T> T setValueFromNew(Class<T> clazz,Object... values) {
		T obj = getT(clazz);
		return setValue(obj,values);
	}

	public static <T> T copyBeanFromNew(Object from,Class<T> clazz) {
		return copyBeanFromNew(from, clazz, null);
	}


	public static <T> T copyBeanFromNew(Object from,Class<T> clazz,String... ignores) {
		T obj = getT(clazz);
		org.springframework.beans.BeanUtils.copyProperties(from,obj,ignores);
		return obj;
	}

	private static <T> T getT(Class<T> clazz) {
		T obj;
		try {
			obj = clazz.newInstance();
		} catch (InstantiationException var4) {
			throw new RuntimeException(var4);
		} catch (IllegalAccessException var5) {
			throw new RuntimeException(var5);
		}
		return obj;
	}

	public static <T> Map<String,T> setMap(Map<String, T> paramMap, Class<T> clazz,Object... keysAndValues){
		for (int i = 0; i < keysAndValues.length ; i += 2) {
			paramMap.put((String)keysAndValues[i],clazz.cast(keysAndValues[i+1]));
		}
		return paramMap;
	}

	public static <T> Map<String,T> setMapNotNull(Map<String, T> paramMap, Class<T> clazz,Object... keysAndValues){
		for (int i = 0; i < keysAndValues.length ; i += 2) {
			Object value = keysAndValues[i + 1];
			if (value != null)
				paramMap.put((String)keysAndValues[i],clazz.cast(value));
		}
		return paramMap;
	}


	public static <T> Map<String,T> setMapFromNew(Class<T> clazz, Object... keysAndValues){
		return setMap(Maps.<String,T>newHashMap(), clazz, keysAndValues);
	}

	public static <T> Map<String,T> setMapFromNewNotNull(Class<T> clazz, Object... keysAndValues){
		return setMapNotNull(Maps.<String,T>newHashMap(), clazz, keysAndValues);
	}

	public static <T> List<T> setValueList(List<T> origin, Object... values) {
		for (T f : origin) {
			BeanUtils.setValue(f, values);
		}
		return origin;
	}

	public static <T,K> List<K> setValueListFromNew(List<T> origin, Class<K> clazz, Object... values) {
		List<K> list = new ArrayList<>();
		for (T f : origin) {
			Object[] a = new Object[values.length];
			for (int i = 0; i < values.length; i += 2) {
				a[i+1] = values[i+1];
				try {
					a[i] = PropertyUtils.getProperty(f, (String)(values[i]));
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    throw new RuntimeException(e);
                }
			}
			list.add(BeanUtils.setValueFromNew(clazz, a));
		}
		return list;
	}

	public static <T> List<T> copyBeanFromNewList(List<?> origin,Class<T> clazz,String... ignores) {
		List<T> newArrayList = Lists.newArrayList();
		for (Object f : origin) {
			newArrayList.add(copyBeanFromNew(f, clazz, ignores));
		}
		return newArrayList;
	}

	public static <T> List<Map<String, T>> setMapList(List<Map<String, T>> origin, Class<T> clazz,Object... keysAndValues) {
		for (Map<String, T> f : origin) {
			setMap(f, clazz, keysAndValues);
		}
		return origin;
	}

	private static String[] getNullPropertyNames (Object source) {
		final BeanWrapper src = new BeanWrapperImpl(source);
		java.beans.PropertyDescriptor[] pds = src.getPropertyDescriptors();

		Set<String> emptyNames = new HashSet<String>();
		for(java.beans.PropertyDescriptor pd : pds) {
			Object srcValue = src.getPropertyValue(pd.getName());
			if (srcValue == null) emptyNames.add(pd.getName());
		}
		String[] result = new String[emptyNames.size()];
		return emptyNames.toArray(result);
	}

	public static void copyPropertiesIgnoreNull(Object src, Object target){
		org.springframework.beans.BeanUtils.copyProperties(src, target, getNullPropertyNames(src));
	}

	public static void main(String[] args) {
		Map<String, Object> stringIntegerMap = setMapFromNew(Object.class, "aaa", null, "bbb", 222,"ccc","123123");
		System.out.println(JSON.toJSONString(stringIntegerMap));
		Test a = new Test();
		;
		System.out.println(JSON.toJSONString(setValueFromNew(Test.class,"id",12L,"cityId",12312L,"customerId","测试","customerId2",null,"flowAgentId",new BigDecimal(1),
				"houseId",11,"ifAgentId",true,"orderState",1233,"orderStates",new Integer[]{1,2,3},"orderStateExtra",new Date())));
	}

	public static class Test {
		private Long id;
		private Long cityId;
		private String customerId;
		private String customerId2;
		private BigDecimal flowAgentId;
		private int houseId;
		private boolean ifAgentId;
		private Integer orderState;
		private Integer[] orderStates;
		private Date orderStateExtra;

		public Long getId() {
			return id;
		}

		public void setId(Long id) {
			this.id = id;
		}

		public Long getCityId() {
			return cityId;
		}

		public void setCityId(Long cityId) {
			this.cityId = cityId;
		}

		public String getCustomerId() {
			return customerId;
		}

		public void setCustomerId(String customerId) {
			this.customerId = customerId;
		}

		public String getCustomerId2() {
			return customerId2;
		}

		public void setCustomerId2(String customerId2) {
			this.customerId2 = customerId2;
		}

		public BigDecimal getFlowAgentId() {
			return flowAgentId;
		}

		public void setFlowAgentId(BigDecimal flowAgentId) {
			this.flowAgentId = flowAgentId;
		}

		public int getHouseId() {
			return houseId;
		}

		public void setHouseId(int houseId) {
			this.houseId = houseId;
		}

		public boolean isIfAgentId() {
			return ifAgentId;
		}

		public void setIfAgentId(boolean ifAgentId) {
			this.ifAgentId = ifAgentId;
		}

		public Integer getOrderState() {
			return orderState;
		}

		public void setOrderState(Integer orderState) {
			this.orderState = orderState;
		}

		public Integer[] getOrderStates() {
			return orderStates;
		}

		public void setOrderStates(Integer[] orderStates) {
			this.orderStates = orderStates;
		}

		public Date getOrderStateExtra() {
			return orderStateExtra;
		}

		public void setOrderStateExtra(Date orderStateExtra) {
			this.orderStateExtra = orderStateExtra;
		}
	}
}
